
/**
* @Title: AutoMetricsAspect.java
* @Package com.leetone.metrics.aop
* @Description: 【】
* @date 2018年5月10日 下午3:21:33
* @version V1.0
* @Company: 上海未泰信息技术有限公司
* @author:[谭磊][leetone123@163.com]
*/

package com.leetone.metrics.aop;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.codahale.metrics.Counter;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Metered;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.codahale.metrics.annotation.Counted;
import com.codahale.metrics.annotation.ExceptionMetered;
import com.codahale.metrics.annotation.Timed;


/**
* @ClassName: AutoMetricsAspect
* @Description: 【】
* @date 2018年5月10日 下午3:21:33
* @Company: 上海未泰信息技术有限公司
* @author:[谭磊][leetone123@163.com]
*/
@Component
@Aspect
public class AutoMetricsAspect {
	
	protected ConcurrentMap<String, Meter> meters = new ConcurrentHashMap<>();
	protected ConcurrentMap<String, Meter> exceptionMeters = new ConcurrentHashMap<>();
	protected ConcurrentMap<String, Timer> timers = new ConcurrentHashMap<>();
	protected ConcurrentMap<String, Counter> counters = new ConcurrentHashMap<>();
	
	@Autowired
	MetricRegistry metricRegistry;
	
	@Pointcut(value="execution(public * *(..))")
	public void publishMethods()
	{
		
	}
	
	@Before("publishMethods() && @annotation(countedAnnotation)")
	public void instrumentCounted(JoinPoint jp,Counted countedAnnotation)
	{
		String name = name(jp.getTarget().getClass(), StringUtils.hasLength(countedAnnotation.name()) ? countedAnnotation.name() : jp.getSignature().getName(), "counter");
		Counter counter = counters.computeIfAbsent(name, key -> metricRegistry.counter(key));
		counter.inc();
	}
	@Before("publishMethods() && @annotation(meteredAnnotation)")
	public void instrumentCounted(JoinPoint jp,Metered meteredAnnotation)
	{
		String name = name(jp.getTarget().getClass(), StringUtils.hasLength(meteredAnnotation.name()) ? meteredAnnotation.name() : jp.getSignature().getName(), "meter");
		Meter meter = meters.computeIfAbsent(name, key -> metricRegistry.meter(key));
		meter.mark();
	}
	
	@AfterThrowing(pointcut = "publishMethods() && @annotation(exMeteredAnnotation)",throwing = "ex")
	public void instrumentExceptionMetered(JoinPoint jp,Throwable ex, ExceptionMetered exMeteredAnnotation)
	{
		String name = name(jp.getTarget().getClass(), StringUtils.hasLength(exMeteredAnnotation.name()) ? exMeteredAnnotation.name() : jp.getSignature().getName(), "meter", "exception");
		Meter meter = exceptionMeters.computeIfAbsent(name, meterName -> metricRegistry.meter(meterName));
		meter.mark();
	}
	
	@Around("publishMethods() && @annotation(timedAnnotation)")
	public Object instrumentTimed(ProceedingJoinPoint pjp,Timed timedAnnotation)
	{
		String name = name(pjp.getTarget().getClass(), StringUtils.hasLength(timedAnnotation.name()) ? timedAnnotation.name() : pjp.getSignature().getName(), "timer");
		Timer timer = timers.computeIfAbsent(name, inputName -> metricRegistry.timer(inputName));
		Timer.Context tc = timer.time();
		try {
			return pjp.proceed();
		}finally {
			tc.stop();
		}
	}
}
